﻿using LiteBlog.Common.Entities;
using LiteBlog.Common.Enums;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using LiteBlog.Repositories;
using LiteBlog.Repositories.Common;

namespace LiteBlog.Repositories.Tests
{
    [TestClass]
    public class PostDataTests : RepositoryTests<PostRepository>
    {
        private static readonly string FileId = "TestId";
        private static readonly string Title = "Test";
        private static readonly string Author = "Test Author";
        private static readonly string FormattedTime = "14-Feb-2013 10:21 AM";
        private static readonly DateTime Time = new DateTime(2013, 02, 14, 10, 21, 0);
        private static readonly string Content = "&lt;p&gt;Welcome to my blog!&lt;/p&gt;";
        private static readonly string Meta = "Test post";
        private static readonly string CommentId = "100";
        private static readonly string CommentAuthor = "Comment Author";
        private static readonly string Comment = "Comment";

        [TestMethod]
        public void WhenLoadedPathIsCorrect()
        {
            _repository.Load(FileId);
            string relativePath = string.Format("Posts\\{0}.xml", FileId);
            string expectedPath = Path.Combine(AssemblySetup.DataPath, relativePath);
            Assert.AreEqual(expectedPath, _repository.Path);
        }

        [TestMethod]
        public void WhenLoadedPostAttributesAreCorrect()
        {
            var post = _repository.Load(FileId);
            Assert.AreEqual(FileId, post.FileID);
            Assert.AreEqual(Title, post.Title);
            Assert.AreEqual(Author, post.Author);
            Assert.AreEqual(Time, post.Time);
            Assert.AreEqual(Time, post.UpdatedTime);
        }

        [TestMethod]
        public void WhenLoadedCategoriesAreCorrect()
        {
            var post = _repository.Load(FileId);
            Assert.AreEqual(2, post.Categories.Count);
            var category = post.Categories[0];
            Assert.AreEqual("Cat1", category.CatID);
            Assert.AreEqual("Category 1", category.Name);
        }

        [TestMethod]
        public void WhenLoadedTagsAreCorrect()
        {
            var post = _repository.Load(FileId);
            Assert.AreEqual(2, post.Tags.Count());
            var tag = post.Tags.ElementAt(0);
            Assert.AreEqual("Tag1", tag.TagId);
            Assert.AreEqual("Tag 1", tag.Name);
        }

        [TestMethod]
        public void WhenLoadedContentIsCorrect()
        {
            var post = _repository.Load(FileId);
            Assert.AreEqual(Content, post.Contents);
        }

        [TestMethod]
        public void WhenLoadedMetaIsCorrect()
        {
            var post = _repository.Load(FileId);
            Assert.AreEqual(Meta, post.Meta);
        }

        [TestMethod]
        public void WhenLoadedCommentsAreLoaded()
        {
            var post = _repository.Load(FileId);
            Assert.AreEqual(1, post.Comments.Count);
            var comment = post.Comments[0];
            Assert.AreEqual(CommentId, comment.ID);
            Assert.AreEqual(CommentAuthor, comment.Name);
            Assert.AreEqual(Comment, comment.Text);
        }

        [TestMethod]
        public void DeletePostDeletesTheFile()
        {
            _repository.Delete(FileId);
            _mock.Verify(x => x.Delete(It.Is<string>(path => VerifyPath(path, FileId))));
        }

        [TestMethod]
        public void UpdateWorks()
        {
            var postInfo = new PostInfo();
            postInfo.FileID = FileId;
            postInfo.Title = "New Title";
            postInfo.Time = Time;
            postInfo.Categories = new List<Category>();
            postInfo.Author = Author;

            _repository.Update(postInfo);
            _mock.Verify(x => x.Save(It.Is<XElement>(elem => (string)elem.Attribute("Title") == "New Title"), 
                                    It.Is<string>(path => VerifyPath(path, FileId))));
        }

        [TestMethod]
        public void UpdateLoadsPost()
        {
            var postInfo = new PostInfo();
            postInfo.FileID = FileId;
            postInfo.Title = "New Title";
            postInfo.Time = Time;
            postInfo.Categories = new List<Category>();
            postInfo.Author = Author;

            _repository.Update(postInfo);
            _mock.Verify(x => x.Load(It.Is<string>(path => VerifyPath(path, FileId))));
        }

        private bool VerifyPath(string path, string fileId)
        {
            string expectedPath = _repository.DataContext.GetPath(DataStoreType.Post, fileId);
            return expectedPath.Equals(path);
        }

        protected override void SetupLoad()
        {
            var postRoot = new XElement("Post",
                              new XAttribute("FileID", FileId),
                              new XAttribute("Title", Title),
                              new XAttribute("Author", Author),
                              new XAttribute("CatID", "Cat1, Cat2"),
                              new XAttribute("TagID", "Tag1, Tag2"),
                              new XAttribute("Time", FormattedTime),
                              new XAttribute("Comments", 1),
                              new XElement("Content", Content),
                              new XElement("Meta", Meta),
                              new XElement("Comments",
                                  new XElement("Comment",
                                      new XAttribute("ID", CommentId),
                                      new XAttribute("Name", CommentAuthor),
                                      new XAttribute("Url", string.Empty),
                                      new XAttribute("Time", FormattedTime),
                                      new XAttribute("Ip", "127.0.0.1"),
                                      new XAttribute("IsAuthor", true),
                                      Comment))
                          );

            string postPath = _repository.DataContext.GetPath(DataStoreType.Post, FileId);
            _mock.Setup(x => x.Load(postPath))
                 .Returns(postRoot);

            string catPath = _repository.DataContext.GetPath(DataStoreType.Category, null);
            var catRoot = new XElement("Categories",
                            new XElement("Category",
                                new XAttribute("ID", "Cat1"),
                                new XAttribute("Name", "Category 1"),
                                new XAttribute("Count", 0),
                                new XAttribute("Order", 1)),
                            new XElement("Category",
                                new XAttribute("ID", "Cat2"),
                                new XAttribute("Name", "Category 2"),
                                new XAttribute("Count", 0),
                                new XAttribute("Order", 2)));

            _mock.Setup(x => x.Load(catPath))
                 .Returns(catRoot);

            string tagPath = _repository.DataContext.GetPath(DataStoreType.Tags, null);
            var tagRoot = new XElement("Tags",
                            new XElement("Tag",
                                new XAttribute("Id", "Tag1"),
                                new XAttribute("Name", "Tag 1"),
                                new XAttribute("Count", 0)),
                            new XElement("Tag",
                                new XAttribute("Id", "Tag2"),
                                new XAttribute("Name", "Tag 2"),
                                new XAttribute("Count", 0)));
            _mock.Setup(x => x.Load(tagPath))
                 .Returns(tagRoot);

            _mock.Setup(x => x.GetInnerXml(It.IsAny<XElement>()))
                 .Returns<XElement>((elem) =>
                 {
                     StringBuilder innerXml = new StringBuilder();
                     elem.Nodes().ToList().ForEach(node => innerXml.Append(node.ToString()));
                     return innerXml.ToString();
                 });

            var blogRoot = new XElement("Blog");
            string blogPath = _repository.DataContext.GetPath(DataStoreType.Blog, null);
            _mock.Setup(x => x.Load(blogPath))
                 .Returns(blogRoot);

            var draftRoot = new XElement("Drafts");
            string draftPath = _repository.DataContext.GetPath(DataStoreType.Draft, null);
            _mock.Setup(x => x.Load(draftPath))
                 .Returns(draftRoot);
        }

        protected override string GetFileName()
        {
            throw new NotImplementedException();
        }

        [Ignore]
        public override void PathIsCorrect()
        {

        }
    }
}
